package com._54programer.ws.handler;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com._54programer.config.HazelcastRunner;
import com._54programer.entity.Token;
import com._54programer.ws.Const;
import com._54programer.ws.Message;
import com._54programer.ws.NBind;
import com._54programer.ws.NSend;
import com._54programer.ws.SpringContextUtils;
import com._54programer.ws.User;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @author SN
 * @date Created by on 2019/11/29
 * @description 处理消息的handler
 *              TextWebSocketFrame：在netty中，是用于为websocket专门处理文本的对象，frame是消息的载体
 */
@Slf4j
public class WsServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

	public static final String GROUP_ID = "netty-gp";

	// 保存和管理所有客户端的channel
	public static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
	// groupId保持活跃
	public static Map<String, Set<String>> groups = new ConcurrentHashMap<String, Set<String>>();
	// groupId保持活跃 gpid-uid
	public static Map<String, Channel> gpid_uid = new ConcurrentHashMap<String, Channel>();
	// username保持活跃
	public static Map<String, Channel> users = new ConcurrentHashMap<String, Channel>();
	// uid保持活跃
	public static Map<String, Channel> uids = new ConcurrentHashMap<String, Channel>();

	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		if (null != msg && msg instanceof FullHttpRequest) {
			// log.info("准备提取token"); // 转化为http请求
			FullHttpRequest request = (FullHttpRequest) msg; // 拿到请求地址
			String uri = request.uri(); // 判断是不是websocket请求，如果是拿出我们传递的参数（我的是token）
			String origin = request.headers().get("Origin");
			if (null == origin) {
				ctx.close();
			} else {
				if (null != uri && uri.contains("/ws") && uri.contains("?")) {
					String[] uriArray = uri.split("\\?");
					if (null != uriArray && uriArray.length > 1) {
						String[] paramsArray = uriArray[1].split("=");
						if (null != paramsArray && paramsArray.length > 1) {
							//log.info("提取参数成功 {}：{}", paramsArray[0], paramsArray[1]);
							String token = paramsArray[1].trim();
							
							//Token token_ = new Token();
							Object token_ = null;
							try {
								token_ = HazelcastRunner.ExpHi.getMap(Const.TOKEN).get(token);
							} catch (Exception e) {
								ctx.close();
								log.error("无效token,禁止获得连接!");
							}
							
							Token tk = JSON.toJavaObject(JSONObject.parseObject(token_.toString()), Token.class);
							//异地登陆，重复登陆
							if(users.containsKey(tk.getToken_username())) {
								//在线
								new NSend().OKbyGroup_UserName(tk.getToken_username(), "重复登录!");
								String cid = users.get(tk.getToken_username()).id().asShortText();
								Channel channel = users.get(tk.getToken_username());
								NBind.unbindUser(tk.getToken_username());//解绑username
								NBind.unbindUid(tk.getToken_uid());//解绑uid
								NBind.unbindGroup(cid);//从group队列中移除
								
								channel.close();
								clients.remove(channel);

								log.info("当前在线clients.size：{}",clients.size());
								log.info("当前在线clients：{}",clients.toArray());
								log.info("当前在线username：{}",users);
								log.info("当前在线uid：{}",uids);
								log.info("当前在线groups：{}",groups);
								log.info("-----------提下线结束-----------------------------");
							}
							
							//NBind.bindGroup(ctx, GROUP_ID);// 广播组
							// 绑定用户username
							NBind.bindUser(ctx, tk.getToken_username());
							// 绑定用户UID
							NBind.bindUid(ctx, tk.getToken_uid());
							// 绑定属性到channel
							User uu = new User(tk.getToken_uid(), tk.getToken_username(), token);
							
							//log.info("用户: {}",JSON.toJSON(uu));
							ctx.channel().attr(AttributeKey.valueOf("USER")).set(uu);
							//log.info("数据绑定： {}",ctx.channel().attr(AttributeKey.valueOf("USER")).get());
							
						}
					}
					request.setUri("/ws");// 重新设置请求地址
				}
			}
		}
		super.channelRead(ctx, msg); // 接着建立请求
	}

	
	
	@Override
	protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {

		// 1.获取客户端发送的消息
		String content = msg.text();
		Channel channel = ctx.channel();
		
		try {
			if (!StringUtil.isNullOrEmpty(content)) {
				try {
					JSONObject req = JSONObject.parseObject(content);
					Message mess = JSON.toJavaObject(req, Message.class);

					// 绑定UUID
					mess.setChannelId(channel.id().asLongText());
					mess.setUuId(channel.id().asShortText());

					String command = mess.getCommand();

					if (command.equals(Const.HEART_BEAT)) {
						// log.info("心跳消息");
						return;
					}

					// ------------------------------------------------------
					JSONObject data = mess.getData();
					String groupId = data.getString("groupId");
					// 绑定组
					NBind.bindGroup(ctx, groupId);
					log.info("groups：" + groups);
					// ---------------------------------------------
//					try {
//						new CBind().bindCGroup(ctx, groupId);
//					} catch (Exception e) {
//						e.printStackTrace();
//						new NSend().OK(ctx, "无效群组ID:" + groupId);
//					}

					//log.info("数据绑定： {}",ctx.channel().attr(AttributeKey.valueOf("USER")).get());
					// 调用对应业务方法
					try {
						Object bean = SpringContextUtils.getBean(command);
						Class<?> cls = bean.getClass();
						// getMethod ,仅限于调用public方法
						// getDeclaredMethod,无限制
						Method method = cls.getMethod("execute", JSONObject.class);
						method.invoke(bean, mess.getData());
					} catch (Exception e) {
						log.error("无效指令：{}", command);
						new NSend().OK(ctx, "无效指令:" + command);
					}

				} catch (Exception e) {
					log.error("json格式错误：{}", content);
					new NSend().OK(ctx, "json格式错误：{}" + content);
				}

			}
		} catch (Exception e) {
			log.error("消息不合法：{}", e);
		}
	}

	/**
	 * 有新客户端连接服务端 获取客户端channel, 添加到ChannelGroup中去管理
	 * 
	 * @param ctx
	 * @throws Exception
	 * 
	 */
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		clients.add(ctx.channel());
		log.info("用户{}上线!", ctx.channel().remoteAddress());
		// 绑定uuId
		//ctx.channel().attr(AttributeKey.valueOf("UUID")).set(ctx.channel().id());
		//log.info("数据绑定成功： {}",ctx.channel().attr(AttributeKey.valueOf("USER")).get());
	}

	/**
	 * 客户端离开
	 */
	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
		User uu = (User) ctx.channel().attr(AttributeKey.valueOf("USER")).get();
		if (uu == null) {
			return;
		}
		//NBind.unbindGroup(ctx, uu.getGroupId());
		//NBind.unbindGroup(users.get(uu.getUsername()).id().asShortText());//从group队列中移除
		NBind.unbindUser(ctx, uu.getUsername());
		NBind.unbindUid(ctx, uu.getUId());
	}

	/**
	 * 异常 1.关闭channel 2.从ChannelGroup移除
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

		log.info("用户{}下线!", ctx.channel().id());

		User uu = (User) ctx.channel().attr(AttributeKey.valueOf("USER")).get();
		if (uu == null) {
			return;
		}
		//NBind.unbindGroup(ctx, uu.getGroupId());
		//NBind.unbindGroup(users.get(uu.getUsername()).id().asShortText());//从group队列中移除
		NBind.unbindUser(ctx, uu.getUsername());
		NBind.unbindUser(ctx, uu.getUId());
		ctx.close();
		clients.remove(ctx.channel());
	}
}